﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using UCL.Powersource.Analysis;


namespace UCL.Powersource.Statistics
{
    /// <summary>
    /// Work items statistics base class
    /// </summary>
    [Export(typeof(IStatistic))]
    [DataContract(IsReference = true)]
    public abstract class WIStateStatisticsBase : StatisticTeamBase
    {
        public WIStateStatisticsBase()
            : base()
        {
            _Log = LogManager.GetLog(typeof(WIStateStatisticsBase));
        }

        private ILog _Log;
        protected ILog Log
        {
            get
            {
                if (_Log == null)
                {
                    _Log = LogManager.GetLog(this.GetType());
                }
                return _Log;
            }
        }

        protected Dictionary<string, Dictionary<string, Dictionary<DateTime, WorkItemCollection>>> collectionCache;     // Work item collection cache

        protected abstract string QueryString { get; }      // Work item query string

        protected abstract string WIType { get; }       // Work item type

        protected abstract string State { get; }        // Work item state

        protected abstract string WasEver { get; }      // Work item state that was before

        [DataMember]
        private string _FormatString = "0";

        public override string FormatString
        {
            get
            {
                return _FormatString;
            }
        }
        /// <summary>
        /// Class for Tfs stat project configuration and work item time frame calculation
        /// </summary>
        /// <param name="teamProjConfig">Tfs stat project configuration</param>
        /// <param name="timeFrame">Work item time frame</param>
        /// <returns>Number of stat project configuration, work item time frame, work item query</returns>
        protected override double? CalculateFor(ITfsTeamProjectConfiguration teamProjConfig, ITimeFrame timeFrame)
        {
            return CalculateStatistic(teamProjConfig, timeFrame, QueryString);
        }

        /// <summary>
        /// Class fot Tfs stat project confguration, work item time frame and work item query string calculation
        /// </summary>
        /// <param name="teamProjConfig">Tfs stat project configuration</param>
        /// <param name="timeFrame">Work item time frame</param>
        /// <param name="qString">Work item query string</param>
        /// <returns>Number of work item iteration paths</returns>
        protected virtual double? CalculateStatistic(ITfsTeamProjectConfiguration teamProjConfig, ITimeFrame timeFrame, string qString)
        {
            ITimeFrameIteration iterationTimeFrame = (ITimeFrameIteration)timeFrame;        // Declaration of work item iteration time frame
            string iterationPath = iterationTimeFrame.GetIterationPathForProject(teamProjConfig);       // Work itwm iteration path
            if (iterationTimeFrame.FinishDate == null || iterationTimeFrame.StartDate == null) return null;


            float count = 0;
            if (timeFrame is ITimeFrameIteration)
            {
                WorkItemCollection queryResults = GetQueryResults(teamProjConfig, timeFrame, qString);      // Get the query results of Tfs stat project configuration, work item time frame and work item query string

                if (queryResults == null) return null;      // If the query results is null, then return null


                int ipcount = 0;
                string itemIPath = "";

                foreach (WorkItem item in queryResults)     // Foreach work item in query results
                {
                    itemIPath = (string)item.Fields["Iteration Path"].Value;
                    if (itemIPath.StartsWith(iterationPath))
                    {
                        ipcount++;      // Count the number of work item iteration paths
                        if (ValidState(item)) count++;
                    }
                }

                if (ipcount == 0) return null;      // If the number of work item iteration paths is equal to null, then return null
            }
            else
            {
                Log.Error(new ArgumentException(), "ITimeFrameDateTime not a valid argument, not implemented yet.", timeFrame);     // Throw an exception "ITimeFrameDateTime not a valid argument, not implemented yet."
                return null;
            }
            return count;       // Return the number of work item iteration paths
        }

        /// <summary>
        /// Class for validation of the work item state
        /// </summary>
        /// <param name="item">Work item</param>
        /// <returns>Work item state</returns>
        protected virtual bool ValidState(WorkItem item)
        {
            return (string)item.Fields["State"].Value == State;
        }

        /// <summary>
        /// Class for Tfs stat project configuration, work item time frame and work item query string, query results
        /// </summary>
        /// <param name="teamProjConfig">Tfs stat project collection configuration</param>
        /// <param name="timeFrame">Work item time frame</param>
        /// <param name="qString">Work item query string</param>
        /// <returns>Results of the query of the work items store, query string and variables</returns>
        protected WorkItemCollection GetQueryResults(ITfsTeamProjectConfiguration teamProjConfig, ITimeFrame timeFrame, string qString)
        {
            ITimeFrameIteration iterationTimeFrame = (ITimeFrameIteration)timeFrame;        // Declaration of work item time frame
            WorkItemCollection queryResults;
            string colUri = teamProjConfig.TeamProjectCollectionConfiguration.Uri;      // Tfs stat project collection configuration uri
            DateTime finishDate = (DateTime)iterationTimeFrame.FinishDate;      // Finish date of the work item iteration time frame
            string iterationPath = iterationTimeFrame.GetIterationPathForProject(teamProjConfig);       // Iteration path
            if (collectionCache == null) collectionCache = new Dictionary<string, Dictionary<string, Dictionary<DateTime, WorkItemCollection>>>();
            if (!collectionCache.ContainsKey(colUri))       // If there is no Uri in the collectionCache
            {
                collectionCache.Add(colUri, new Dictionary<string, Dictionary<DateTime, WorkItemCollection>>());        // then add colUri to the new Dictionary
                collectionCache[colUri].Add(iterationPath, new Dictionary<DateTime, WorkItemCollection>());     // add work item iteration path to the new Dictionary
            }
            else
            {
                if (!collectionCache[colUri].ContainsKey(iterationPath))       // otherwise if there is no work item iteration path
                {
                    collectionCache[colUri].Add(iterationPath, new Dictionary<DateTime, WorkItemCollection>());     // then add work item iteration path to the new Dictionary
                }
            }

            if (collectionCache[colUri][iterationPath].ContainsKey(finishDate) &&
                collectionCache[colUri][iterationPath][finishDate].Count != 0)      // if there is a Finish date in the Iteration path and the number of finish date of the iteration path is not equal to null
            {
                queryResults = collectionCache[colUri][iterationPath][finishDate];      // then get the finish date of iteration path
            }
            else
            {
                var wiStore = StatisticsUtilities.GetWIStore(teamProjConfig);       // otherwise get the work item store of the tfs stat project configuration

                Dictionary<string, Object> variables = SetQueryStringVariables(iterationTimeFrame);
                if (WIType == "Product Backlog Item")       // if the work item type is Product Backlog Item
                {
                    variables.Add("IterationPath", iterationPath);      // then add Iteration Path to the variables
                    queryResults = StatisticsUtilities.GetPBIs(wiStore, variables, true);
                }
                else
                {
                    try
                    {
                        Query query = new Query(wiStore, qString, variables);       // otherwise query for work item store, query string and variables
                        queryResults = query.RunQuery();        // run the query
                    }
                    catch (ValidationException exc)
                    {
                        Log.Error(exc, "Invalid arguments for query in GetQueryResults() for collection {0}.", colUri);     // Throw an exception "Invalid arguments for query in GetQueryResults() for collection {0}."
                        return null;
                    }
                }
            }
            if (queryResults != null)
            {
                if (!collectionCache[colUri][iterationPath].ContainsKey(finishDate))       // if the query results is not equal to null
                    collectionCache[colUri][iterationPath].Add(finishDate, queryResults);       // then add finish date to the iteration path
                else
                    collectionCache[colUri][iterationPath][finishDate] = queryResults;
            }

            return queryResults;
        }

        /// <summary>
        /// Class for variables query
        /// </summary>
        /// <param name="timeframe">Work item time frame</param>
        /// <returns>Variables Asof, WIType, WasEver</returns>
        protected virtual Dictionary<string, object> SetQueryStringVariables(ITimeFrameIteration timeframe)
        {
            Dictionary<string, Object> variables = new Dictionary<string, Object>();        // Creation of the new Dictionary

            DateTime AsOf = GetAsOfDate(timeframe);
            variables.Add("AsOf", AsOf);        // Add to the variables As of the work item state
            variables.Add("WIType", WIType);        // Add to the variables Work item type
            variables.Add("WasEver", WasEver);      // Add to the variables the state of the work item that was before

            return variables;
        }

        /// <summary>
        /// Class of the current date of the iteration time frame
        /// </summary>
        /// <param name="timeframe">Work item time frame</param>
        /// <returns></returns>
        protected virtual DateTime GetAsOfDate(ITimeFrameIteration timeframe)
        {
            return StatisticsUtilities.GetAsOfDate(timeframe);
        }

        public override bool RequiresBuild
        {
            get { return false; }
        }
    }
}
